1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.collect;
18
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.base.Predicates.in;
21 import static com.google.common.base.Predicates.not;
22 import static com.google.common.collect.CollectPreconditions.checkNonnegative;
23
24 import com.google.common.annotations.GwtCompatible;
25 import com.google.common.base.MoreObjects;
26 import com.google.common.base.Predicate;
27 import com.google.common.collect.Maps.ImprovedAbstractMap;
28
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.Set;
36
37 import javax.annotation.Nullable;
38
39
40
41
42
43
44
45 @GwtCompatible
46 class FilteredEntryMultimap<K, V> extends AbstractMultimap<K, V> implements FilteredMultimap<K, V> {
47 final Multimap<K, V> unfiltered;
48 final Predicate<? super Entry<K, V>> predicate;
49
50 FilteredEntryMultimap(Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> predicate) {
51 this.unfiltered = checkNotNull(unfiltered);
52 this.predicate = checkNotNull(predicate);
53 }
54
55 @Override
56 public Multimap<K, V> unfiltered() {
57 return unfiltered;
58 }
59
60 @Override
61 public Predicate<? super Entry<K, V>> entryPredicate() {
62 return predicate;
63 }
64
65 @Override
66 public int size() {
67 return entries().size();
68 }
69
70 private boolean satisfies(K key, V value) {
71 return predicate.apply(Maps.immutableEntry(key, value));
72 }
73
74
75 final class ValuePredicate implements Predicate<V> {
76 private final K key;
77
78 ValuePredicate(K key) {
79 this.key = key;
80 }
81
82 @Override
83 public boolean apply(@Nullable V value) {
84 return satisfies(key, value);
85 }
86 }
87
88 static <E> Collection<E> filterCollection(
89 Collection<E> collection, Predicate<? super E> predicate) {
90 if (collection instanceof Set) {
91 return Sets.filter((Set<E>) collection, predicate);
92 } else {
93 return Collections2.filter(collection, predicate);
94 }
95 }
96
97 @Override
98 public boolean containsKey(@Nullable Object key) {
99 return asMap().get(key) != null;
100 }
101
102 @Override
103 public Collection<V> removeAll(@Nullable Object key) {
104 return MoreObjects.firstNonNull(asMap().remove(key), unmodifiableEmptyCollection());
105 }
106
107 Collection<V> unmodifiableEmptyCollection() {
108
109 return (unfiltered instanceof SetMultimap)
110 ? Collections.<V>emptySet()
111 : Collections.<V>emptyList();
112 }
113
114 @Override
115 public void clear() {
116 entries().clear();
117 }
118
119 @Override
120 public Collection<V> get(final K key) {
121 return filterCollection(unfiltered.get(key), new ValuePredicate(key));
122 }
123
124 @Override
125 Collection<Entry<K, V>> createEntries() {
126 return filterCollection(unfiltered.entries(), predicate);
127 }
128
129 @Override
130 Collection<V> createValues() {
131 return new FilteredMultimapValues<K, V>(this);
132 }
133
134 @Override
135 Iterator<Entry<K, V>> entryIterator() {
136 throw new AssertionError("should never be called");
137 }
138
139 @Override
140 Map<K, Collection<V>> createAsMap() {
141 return new AsMap();
142 }
143
144 @Override
145 public Set<K> keySet() {
146 return asMap().keySet();
147 }
148
149 boolean removeEntriesIf(Predicate<? super Entry<K, Collection<V>>> predicate) {
150 Iterator<Entry<K, Collection<V>>> entryIterator = unfiltered.asMap().entrySet().iterator();
151 boolean changed = false;
152 while (entryIterator.hasNext()) {
153 Entry<K, Collection<V>> entry = entryIterator.next();
154 K key = entry.getKey();
155 Collection<V> collection = filterCollection(entry.getValue(), new ValuePredicate(key));
156 if (!collection.isEmpty() && predicate.apply(Maps.immutableEntry(key, collection))) {
157 if (collection.size() == entry.getValue().size()) {
158 entryIterator.remove();
159 } else {
160 collection.clear();
161 }
162 changed = true;
163 }
164 }
165 return changed;
166 }
167
168 class AsMap extends ImprovedAbstractMap<K, Collection<V>> {
169 @Override
170 public boolean containsKey(@Nullable Object key) {
171 return get(key) != null;
172 }
173
174 @Override
175 public void clear() {
176 FilteredEntryMultimap.this.clear();
177 }
178
179 @Override
180 public Collection<V> get(@Nullable Object key) {
181 Collection<V> result = unfiltered.asMap().get(key);
182 if (result == null) {
183 return null;
184 }
185 @SuppressWarnings("unchecked")
186 K k = (K) key;
187 result = filterCollection(result, new ValuePredicate(k));
188 return result.isEmpty() ? null : result;
189 }
190
191 @Override
192 public Collection<V> remove(@Nullable Object key) {
193 Collection<V> collection = unfiltered.asMap().get(key);
194 if (collection == null) {
195 return null;
196 }
197 @SuppressWarnings("unchecked")
198 K k = (K) key;
199 List<V> result = Lists.newArrayList();
200 Iterator<V> itr = collection.iterator();
201 while (itr.hasNext()) {
202 V v = itr.next();
203 if (satisfies(k, v)) {
204 itr.remove();
205 result.add(v);
206 }
207 }
208 if (result.isEmpty()) {
209 return null;
210 } else if (unfiltered instanceof SetMultimap) {
211 return Collections.unmodifiableSet(Sets.newLinkedHashSet(result));
212 } else {
213 return Collections.unmodifiableList(result);
214 }
215 }
216
217 @Override
218 Set<K> createKeySet() {
219 return new Maps.KeySet<K, Collection<V>>(this) {
220 @Override
221 public boolean removeAll(Collection<?> c) {
222 return removeEntriesIf(Maps.<K>keyPredicateOnEntries(in(c)));
223 }
224
225 @Override
226 public boolean retainAll(Collection<?> c) {
227 return removeEntriesIf(Maps.<K>keyPredicateOnEntries(not(in(c))));
228 }
229
230 @Override
231 public boolean remove(@Nullable Object o) {
232 return AsMap.this.remove(o) != null;
233 }
234 };
235 }
236
237 @Override
238 Set<Entry<K, Collection<V>>> createEntrySet() {
239 return new Maps.EntrySet<K, Collection<V>>() {
240 @Override
241 Map<K, Collection<V>> map() {
242 return AsMap.this;
243 }
244
245 @Override
246 public Iterator<Entry<K, Collection<V>>> iterator() {
247 return new AbstractIterator<Entry<K, Collection<V>>>() {
248 final Iterator<Entry<K, Collection<V>>> backingIterator
249 = unfiltered.asMap().entrySet().iterator();
250
251 @Override
252 protected Entry<K, Collection<V>> computeNext() {
253 while (backingIterator.hasNext()) {
254 Entry<K, Collection<V>> entry = backingIterator.next();
255 K key = entry.getKey();
256 Collection<V> collection
257 = filterCollection(entry.getValue(), new ValuePredicate(key));
258 if (!collection.isEmpty()) {
259 return Maps.immutableEntry(key, collection);
260 }
261 }
262 return endOfData();
263 }
264 };
265 }
266
267 @Override
268 public boolean removeAll(Collection<?> c) {
269 return removeEntriesIf(in(c));
270 }
271
272 @Override
273 public boolean retainAll(Collection<?> c) {
274 return removeEntriesIf(not(in(c)));
275 }
276
277 @Override
278 public int size() {
279 return Iterators.size(iterator());
280 }
281 };
282 }
283
284 @Override
285 Collection<Collection<V>> createValues() {
286 return new Maps.Values<K, Collection<V>>(AsMap.this) {
287 @Override
288 public boolean remove(@Nullable Object o) {
289 if (o instanceof Collection) {
290 Collection<?> c = (Collection<?>) o;
291 Iterator<Entry<K, Collection<V>>> entryIterator
292 = unfiltered.asMap().entrySet().iterator();
293 while (entryIterator.hasNext()) {
294 Entry<K, Collection<V>> entry = entryIterator.next();
295 K key = entry.getKey();
296 Collection<V> collection
297 = filterCollection(entry.getValue(), new ValuePredicate(key));
298 if (!collection.isEmpty() && c.equals(collection)) {
299 if (collection.size() == entry.getValue().size()) {
300 entryIterator.remove();
301 } else {
302 collection.clear();
303 }
304 return true;
305 }
306 }
307 }
308 return false;
309 }
310
311 @Override
312 public boolean removeAll(Collection<?> c) {
313 return removeEntriesIf(Maps.<Collection<V>>valuePredicateOnEntries(in(c)));
314 }
315
316 @Override
317 public boolean retainAll(Collection<?> c) {
318 return removeEntriesIf(Maps.<Collection<V>>valuePredicateOnEntries(not(in(c))));
319 }
320 };
321 }
322 }
323
324 @Override
325 Multiset<K> createKeys() {
326 return new Keys();
327 }
328
329 class Keys extends Multimaps.Keys<K, V> {
330 Keys() {
331 super(FilteredEntryMultimap.this);
332 }
333
334 @Override
335 public int remove(@Nullable Object key, int occurrences) {
336 checkNonnegative(occurrences, "occurrences");
337 if (occurrences == 0) {
338 return count(key);
339 }
340 Collection<V> collection = unfiltered.asMap().get(key);
341 if (collection == null) {
342 return 0;
343 }
344 @SuppressWarnings("unchecked")
345 K k = (K) key;
346 int oldCount = 0;
347 Iterator<V> itr = collection.iterator();
348 while (itr.hasNext()) {
349 V v = itr.next();
350 if (satisfies(k, v)) {
351 oldCount++;
352 if (oldCount <= occurrences) {
353 itr.remove();
354 }
355 }
356 }
357 return oldCount;
358 }
359
360 @Override
361 public Set<Multiset.Entry<K>> entrySet() {
362 return new Multisets.EntrySet<K>() {
363
364 @Override
365 Multiset<K> multiset() {
366 return Keys.this;
367 }
368
369 @Override
370 public Iterator<Multiset.Entry<K>> iterator() {
371 return Keys.this.entryIterator();
372 }
373
374 @Override
375 public int size() {
376 return FilteredEntryMultimap.this.keySet().size();
377 }
378
379 private boolean removeEntriesIf(final Predicate<? super Multiset.Entry<K>> predicate) {
380 return FilteredEntryMultimap.this.removeEntriesIf(
381 new Predicate<Map.Entry<K, Collection<V>>>() {
382 @Override
383 public boolean apply(Map.Entry<K, Collection<V>> entry) {
384 return predicate.apply(
385 Multisets.immutableEntry(entry.getKey(), entry.getValue().size()));
386 }
387 });
388 }
389
390 @Override
391 public boolean removeAll(Collection<?> c) {
392 return removeEntriesIf(in(c));
393 }
394
395 @Override
396 public boolean retainAll(Collection<?> c) {
397 return removeEntriesIf(not(in(c)));
398 }
399 };
400 }
401 }
402 }